package cc.shacocloud.mirage.restful.util;


import cc.shacocloud.mirage.restful.HttpRequest;
import cc.shacocloud.mirage.utils.Utils;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import org.jetbrains.annotations.Nullable;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * CORS请求处理的工具类  <a href="https://www.w3.org/TR/cors/">W3C CORS 标准</a>.
 */
public abstract class CorsUtils {
    
    private static final String SCHEME_PATTERN = "([^:/?#]+):";
    
    private static final String USERINFO_PATTERN = "([^@\\[/?#]*)";
    
    private static final String HOST_IPV4_PATTERN = "[^\\[/?#:]*";
    
    private static final String HOST_IPV6_PATTERN = "\\[[\\p{XDigit}:.]*[%\\p{Alnum}]*]";
    
    private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";
    
    
    private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?})?)";
    private static final String PATH_PATTERN = "([^?#]*)";
    private static final String QUERY_PATTERN = "([^#]*)";
    private static final String LAST_PATTERN = "(.*)";
    
    // 匹配uri的正则表达式模式 见 RFC 3986, 附录 B
    private static final Pattern URI_PATTERN = Pattern.compile(
            "^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
                    ")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");
    
    /**
     * 如果请求是有效的CORS，通过检查源头是否存在并确保源是不同的，返回{@code true}
     */
    public static boolean isCorsRequest(HttpRequest request) {
        String origin = request.headers().get(HttpHeaders.ORIGIN);
        if (origin == null) return false;
        
        Matcher matcher = URI_PATTERN.matcher(origin);
        if (!matcher.matches()) {
            throw new IllegalArgumentException("[" + origin + "] 不是有效的 \"Origin\" 请求头");
        }
        
        String originScheme = matcher.group(2);
        String originHost = matcher.group(6);
        String originPortStr = matcher.group(8);
        
        // 端口解析判断
        int originPort;
        if (originPortStr == null) {
            originPort = -1;
        } else if (originPortStr.contains("{")) {
            throw new IllegalStateException("该端口包含一个URI变量，但尚未展开: " + originPortStr);
        } else {
            originPort = Integer.parseInt(originPortStr);
        }
        
        String scheme = request.getScheme();
        String host = request.getServerName();
        int port = request.getServerPort();
        return !(Utils.nullSafeEquals(scheme, originScheme) &&
                Utils.nullSafeEquals(host, originHost) &&
                getPort(scheme, port) == getPort(originScheme, originPort));
    }
    
    private static int getPort(@Nullable String scheme, int port) {
        if (port == -1) {
            if ("http".equals(scheme) || "ws".equals(scheme)) {
                port = 80;
            } else if ("https".equals(scheme) || "wss".equals(scheme)) {
                port = 443;
            }
        }
        return port;
    }
    
    /**
     * 如果请求是一个有效的CORS 执行前检查{code OPTIONS}方法的起源和访问-控制-请求-方法头存在。
     */
    public static boolean isPreFlightRequest(HttpRequest request) {
        return HttpMethod.OPTIONS == request.method() &&
                request.headers().get(HttpHeaders.ORIGIN) != null &&
                request.headers().get(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null;
    }
    
}
